Java分布式全局唯一Id:id生成要求、为什么不用UUID、生成分布式雪花Id 您所在的位置:网站首页 face id为什么不能横着 Java分布式全局唯一Id:id生成要求、为什么不用UUID、生成分布式雪花Id

Java分布式全局唯一Id:id生成要求、为什么不用UUID、生成分布式雪花Id

2024-07-17 08:17| 来源: 网络整理| 查看: 265

文章目录 为什么需要分布式全局唯一IdId生成规则部分硬性要求Id生成系统的可用性要求为什么不用UUID生成分布式雪花IdPOM代码示例API生成18位雪花Id生成13位雪花Id

为什么需要分布式全局唯一Id

在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。

如在美团点评的金融、支付、餐饮、酒店;猫眼电影等产品的系统中数据日渐增长,对数据分库分表后需要有一个唯一Id来标识一条数据或消息;特别一点的如订单、骑手、优惠券也都需要有唯一Id坐标时。

此时一个能够生成全局唯一Id的系统是非常必要的。

Id生成规则部分硬性要求 全局唯一趋势递增:在Mysql的InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用Btree的数据结构来存储索引数据,在主键的选择上-面我们应该尽量使用有序的主键来保证写入性能;单调递增:尽量保证下一个Id一定大于上一个Id,例如事务版本号、IM增量消息、排序等特殊需求;信息安全:如果Id是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量.所以在一些应用场景下,需要Id无规则不规则,让竞争对手不好猜;含时间戳:这样就能够在开发中快速了解这个分布式Id的生成时间。 Id生成系统的可用性要求 高可用:发一个获取分布式Id的请求,服务器就要保证99.999%的情况下给我创建一个唯一分布式Id;低延迟:发一个获取分布式Id的请求,服务器要快,极速;高QPS:例如并发一口气10万个创建分布式Id请求同时杀过来,服务器要顶得住且一下子成功创建10万个分布式Id。 为什么不用UUID

在《阿里巴巴 Java 开发手册》第五章 MySQL 规定第九条中,强制规定了单表的主键 id 必须为无符号的 bigint 类型,且是自增的。MySQL 中索引的数据结构是 B+Tree,这种数据结构的特点是索引树上的节点的数据是有序的,而如果使用 UUID 作为主键,那么每次插入数据时,因为无法保证每次产生的 UUID 有序,所以就会出现新的 UUID 需要插入到索引树的中间去,这样可能会频繁地导致页分裂,使性能下降。

太占用内存。每个 UUID 由 36 个字符组成,在字符串进行比较时,需要从前往后比较,字符串越长,性能越差。另外字符串越长,占用的内存越大,由于页的大小是固定的,这样一个页上能存放的关键字数量就会越少,这样最终就会导致索引树的高度越大,在索引搜索的时候,发生的磁盘 IO 次数越多,性能越差。

生成分布式雪花Id POM cn.hutool hutool-all 5.8.6 代码示例 @Test public void test(){ System.out.println(IdUtil.getSnowflakeNextId()); } API package cn.hutool.core.util; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.ObjectId; import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.Snowflake; import cn.hutool.core.lang.UUID; import cn.hutool.core.lang.id.NanoId; import cn.hutool.core.net.NetUtil; public class IdUtil { public IdUtil() { } public static String randomUUID() { return UUID.randomUUID().toString(); } public static String simpleUUID() { return UUID.randomUUID().toString(true); } public static String fastUUID() { return UUID.fastUUID().toString(); } public static String fastSimpleUUID() { return UUID.fastUUID().toString(true); } public static String objectId() { return ObjectId.next(); } /** @deprecated */ @Deprecated public static Snowflake createSnowflake(long workerId, long datacenterId) { return new Snowflake(workerId, datacenterId); } public static Snowflake getSnowflake(long workerId, long datacenterId) { return (Snowflake)Singleton.get(Snowflake.class, new Object[]{workerId, datacenterId}); } public static Snowflake getSnowflake(long workerId) { return (Snowflake)Singleton.get(Snowflake.class, new Object[]{workerId}); } public static Snowflake getSnowflake() { return (Snowflake)Singleton.get(Snowflake.class, new Object[0]); } public static long getDataCenterId(long maxDatacenterId) { Assert.isTrue(maxDatacenterId > 0L, "maxDatacenterId must be > 0", new Object[0]); if (maxDatacenterId == Long.MAX_VALUE) { --maxDatacenterId; } long id = 1L; byte[] mac = null; try { mac = NetUtil.getLocalHardwareAddress(); } catch (UtilException var6) { } if (null != mac) { id = (255L & (long)mac[mac.length - 2] | 65280L & (long)mac[mac.length - 1] 6; id %= maxDatacenterId + 1L; } return id; } public static long getWorkerId(long datacenterId, long maxWorkerId) { StringBuilder mpid = new StringBuilder(); mpid.append(datacenterId); try { mpid.append(RuntimeUtil.getPid()); } catch (UtilException var6) { } return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L); } public static String nanoId() { return NanoId.randomNanoId(); } public static String nanoId(int size) { return NanoId.randomNanoId(size); } public static long getSnowflakeNextId() { return getSnowflake().nextId(); } public static String getSnowflakeNextIdStr() { return getSnowflake().nextIdStr(); } } 生成18位雪花Id public class SnowFlake { // 起始的时间戳,这个时间戳可以是你的系统初始时间,一般取当前时间戳 private final static long START_TIMESTAMP = 1672502400000L; // 2023-01-01 00:00:00 // 每一部分占用的位数,可以根据自己的需求进行调整,这里是按照默认的占位数进行分配 private final static long SEQUENCE_BIT = 12; // 序列号占用的位数 private final static long MACHINE_BIT = 5; // 机器标识占用的位数 private final static long DATA_CENTER_BIT = 5; // 数据中心占用的位数 // 每一部分的最大值,可以根据占用的位数进行计算得到 private final static long MAX_SEQUENCE = ~(-1L throw new IllegalArgumentException("数据中心标识不能大于等于 " + MAX_DATA_CENTER_NUM + " 或小于 0"); } if (machineId > MAX_MACHINE_NUM || machineId // 获取系统当前时间戳 long currentTimeStamp = getSystemCurrentTimeMillis(); if (currentTimeStamp // 当前毫秒内,序列号自增 sequence = (sequence + 1) & MAX_SEQUENCE; // 序列号超出范围,需要等待下一毫秒 if (sequence == 0L) { // 获取下一毫秒 currentTimeStamp = getNextMill(lastTimeStamp); } } else { // 不同毫秒内,序列号置为 0 sequence = 0L; } lastTimeStamp = currentTimeStamp; // 使用位运算生成最终的 ID return (currentTimeStamp - START_TIMESTAMP) long timeMillis = getSystemCurrentTimeMillis(); while (timeMillis SnowFlake worker1 = new SnowFlake(1, 1); System.out.println(worker1.nextId()); } } 生成13位雪花Id package com.ais.common.web.utils; public class IdUtil { /** * 开始时间截 (本次时间戳为:Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间)----1288834974657L---1656543015264587776--19 ) */ private final long startTime = 1683803335498L; /** * 机器id所占的位数 */ private final long workerIdBits = 3L; /** * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId)); } this.workerId = workerId; } // ==============================Methods========================================== /** * 获得下一个ID (该方法是线程安全的) * * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID return ((timestamp - startTime) timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } public static long getSnowflakeNextId() { return idWorker.nextId(); } /** * 测试 */ public static void main(String[] args) { System.out.println(IdUtil.getSnowflakeNextId()); } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有